package com.example.net1;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.SignatureException;
import java.security.SignedObject;
import java.security.UnrecoverableEntryException;
import java.security.cert.CertificateException;

import javax.crypto.SealedObject;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

public class CashDeskSSL {
	
	public static final String BANK_IP = "127.0.0.1" ;
	public static final String CashDeskReqNonce =  "getNonce";	
	public static final String PaymentRefused =  "Payment Refused";	
	public static final int PORT_ANDROID = 9999;
	public static final int PORT_BANK = 8888;
	
	public static final String keyStore = "./keystore.jks";
	public static final String cert = "./serverCA.crt";
	
	public Long sellerId;	
	
	public CashDeskSSL(long sellerId) {
		this.sellerId = sellerId;
	}

	public void listenForClient() {
		try {
			ServerSocket ss = new ServerSocket(55555);
			s = ss.accept();
			os = s.getOutputStream();
			oos = new ObjectOutputStream(os);
			is = s.getInputStream();
			ois = new ObjectInputStream(is);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
	}
	
	public void disconnect() {
		try {
			os.close();
			oos.close();
			is.close();
			ois.close();
			s.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
	}
	
	private Socket s;
	private OutputStream os;
	private InputStream is;
	private ObjectOutputStream oos;
	private ObjectInputStream ois;
	
	public void disconnectBank() {
		try {
			bos.close();
			boos.close();
			bis.close();
			bois.close();
			bsocket.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	Socket bsocket;
	OutputStream bos;
	InputStream bis;
	ObjectOutputStream boos = null;
	ObjectInputStream bois = null;
	
	// Need to be connected to a client
	public EncryptedObject getEncryptedBankTicket(Receipt receipt, long nonce) {
		EncryptedObject encryptedBankTicket = null;
		try {
			oos.writeObject(receipt);
			// Wait for validation
			int reply = (Integer) ois.readObject();
			if (reply == 1) {
				// Create PaymentRequest
				PaymentRequest paymentRequest =
						new PaymentRequest(receipt.amount, sellerId, nonce);
				try {
					Signature signature = 
							Signature.getInstance("SHA1withRSA"); //cashDeskKeyPair.getPrivate().getAlgorithm());
					
					SignedObject signedObject =
							new SignedObject(
									paymentRequest, CryptoUtil.getRSAprivateKey(keyStore), signature);
					oos.writeObject(signedObject);
				} catch (NoSuchAlgorithmException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (InvalidKeyException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (SignatureException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (CertificateException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (KeyStoreException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (UnrecoverableEntryException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				// Wait for bank ticket
				encryptedBankTicket = (EncryptedObject) ois.readObject();
			}
			else { // Payment refused
				disconnect();
			}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		return encryptedBankTicket;
	}
	
	// Simulation
	public Receipt generateReceipt() {
		Receipt r = new Receipt();
		r.addEntry("Coke 2L", 2, 2); // 2 bottles
		r.addEntry("Cookies", 3, 2);
		r.addEntry("Wheat bread", 4.29, 1);
		r.addEntry("Oatmeal bread", 3.49, 2);
		r.addEntry("Bananas", 0.25, 6);
		r.addEntry("Cheerios", 3.49, 1);
		r.addEntry("Smoked ham", 4.49, 2);
		r.addEntry("Milk", 2.59, 1);
		r.addEntry("Vegetable oil", 2.99, 1);
		r.addEntry("Chicken breasts", 5.59, 2);
		r.addEntry("Salmon", 6.99, 2);
		r.addEntry("Tuna", 2.49, 5);
		return r;
	}
	
	public void connectToBank(){	
		try {
			SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
			SSLSocket bsocket =  (SSLSocket) sslsocketfactory.createSocket(BANK_IP, PORT_BANK);
		//	bsocket = new Socket(BANK_IP, PORT_BANK);
			bis =  bsocket.getInputStream();
			bos =  bsocket.getOutputStream();				
			boos = new ObjectOutputStream(bos);  
			bois = new ObjectInputStream(bis); 			
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}		
	}
	
	public long  getNonce(){	
		long res = 0;
		try {
			boos.writeObject(CashDeskReqNonce);
			boos.flush();
			res = (Long)bois.readObject(); 
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}	
		return res;
	}
	
	public String ForwardBankInfo(Object bankInfos){
		String res = "";
		try {
			boos.writeObject(bankInfos);
			boos.flush();
			res = (String)bois.readObject(); 
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}	
		return res;		
	}
	
	public String sendReceiptToAndroid(Object receiptNumber){
		String res = "";
		try {
			oos.writeObject(receiptNumber);
			oos.flush();			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();		
		}	
		return res;		
	}
	
	public static void main(String[] args) {
		CashDeskSSL cashDesk = new CashDeskSSL(1111222233334444L); // sellerId = 113
		
		// Simulation: generation of a receipt
		Receipt receipt = cashDesk.generateReceipt();
		
		cashDesk.listenForClient();
		
		cashDesk.connectToBank();
		long nonce = cashDesk.getNonce();	
		// generated by the bank	
		System.out.println("nonce is: "+ nonce);
		EncryptedObject eb =cashDesk.getEncryptedBankTicket(receipt, nonce);
		String receiptNumber = cashDesk.ForwardBankInfo(eb);
		System.out.println("receipt is: "+ receiptNumber);
		if(receiptNumber.contains("OK")){
			cashDesk.sendReceiptToAndroid("Receipt Number is: "+receiptNumber.split(":")[1]);
		}
		else {
			cashDesk.sendReceiptToAndroid(PaymentRefused);
		}				
	}	
}